package ua.kpi.fpm.km61.strat.sp.st;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.TreeSet;

import ua.kpi.fpm.km61.strat.sp.la.ResString;

public class SymbolAnalizer {
	
	private Block base;
	private List<ClassName> classes = new ArrayList<ClassName>();
	
	/**
	 * @return the base
	 */
	public Block getBase() {
		return base;
	}

	/**
	 * @param base the base to set
	 */
	public void setBase(Block base) {
		this.base = base;
	}

	public void analize(TreeSet<ResString> lexems){
		ArrayList<ResString> code = new ArrayList<ResString>();
		code.addAll(lexems);
		Block baseBlock = new Block();
		baseBlock.setLevel(0);
		baseBlock.setType(BlockType.MAIN);
		baseBlock.setLevel(0);
		Block curBlock = baseBlock;
		ListIterator<ResString> ls = code.listIterator();
		int count = -1;
	    while(ls.hasNext()){
	    	ResString lexem = ls.next();
	    	count += 1;
	    	if (lexem.getStr().equals("{")){
	    		Block block = new Block();
	    		block.setLevel(curBlock.getLevel()+1);
	    		block.setFirstIndex(count);
	    		block.setOuter(curBlock);
	    		int typeSearchStart = block.getFirstIndex();
	    		for(int i = typeSearchStart; i >=0; i--){
	    			if(code.get(i).getStr().equals("for")){
	    				block.setType(BlockType.FOR);
	    				block.setFirstIndex(i);
	    				break;
	    			}
	    			if(code.get(i).getStr().equals("if")){
	    				block.setType(BlockType.IF);
	    				block.setFirstIndex(i);
	    				break;
	    			}
	    			if(code.get(i).getStr().equals("else")){
	    				block.setType(BlockType.ELSE);
	    				block.setFirstIndex(i);
	    				break;
	    			}
	    			if(code.get(i).getStr().equals("while")){
	    				block.setType(BlockType.WHILE);
	    				block.setFirstIndex(i);
	    				break;
	    			}
	    			if(code.get(i).getLex().equals("ids")){
	    				String type = code.get(i-1).getStr();
		    			if(type.equals("class")){
		    				block.setType(BlockType.CLASS);
		    				Symbol check = block.find(code.get(i).getStr());
		    				if(check != null){
		    					if(check.getBlockType() != BlockType.CLASS){
		    						throw new RuntimeException("Wrong class name");
		    					}
		    				}
		    				Symbol head = new Symbol();
		    				head.setName(code.get(i).getStr());
		    				head.setBlockLevel(block.getOuter().getLevel());
		    				head.setBlockType(block.getOuter().getType());
		    				head.setDeclarated(true);
		    				String type1 = code.get(i-2).getStr();
	    					head.setPrivateSymbol(type1.equals("private"));
	    					block.setName(head.getName());
	    					head.setFirstIndex(i);
	    					ClassName cn = new ClassName();
	    					cn.setName(head);
	    					classes.add(cn);
				    		block.getOuter().addSymbol(head);
		    				break;
		    			}
	    				if (code.get(i-1).getLex().equals("ids")|| type.equals("void") || type.equals("int")|| type.equals("float") || type.equals("boolean")){
	    					String type1 = code.get(i-2).getStr();
	    					if (type1.equals("private") || type1.equals("public")){
		    					block.setType(BlockType.METHOD);
		    					block.setFirstIndex(i);
		    					Symbol check = block.find(code.get(i).getStr());
			    				if(check != null){
			    					if(check.getBlockType() != BlockType.METHOD){
			    						throw new RuntimeException("Wrong method name");
			    					}
			    				}
			    				Symbol head = new Symbol();
			    				head.setName(code.get(i).getStr());
			    				head.setBlockLevel(block.getOuter().getLevel());
			    				head.setBlockType(block.getOuter().getType());
			    				head.setDeclarated(true);
			    				head.setFirstIndex(i);
		    					head.setPrivateSymbol(type1.equals("private"));
		    					for(ClassName cn: classes){
		    						if(cn.getName().getName().equals(block.getName())){
		    							cn.addMethod(head);
		    						}
		    					}
					    		block.getOuter().addSymbol(head);
		    					break;
	    					}

	    				}
	    				if (type.equals("private") || type.equals("public")){
	    					block.setType(BlockType.CONSTRUCTOR);
	    					block.setFirstIndex(i);
		    				Symbol head = new Symbol();
		    				head.setName(code.get(i).getStr());
		    				System.out.println(head.getName());
		    				System.out.println(block.getOuter().getName());
		    				if(!head.getName().equals(block.getOuter().getName())){
		    					throw new RuntimeException("Wrong Constructor/Method declaration");
		    				}
		    				Symbol check = block.find(head.getName());
							if (check != null){
								check.setPrivateSymbol(type.equals("private"));
							}
		    				break;
	    				}
	    			}
	    		}
	    		
	    		curBlock.addInner(block);
	    		curBlock = block;	
	    	}
	    	if (lexem.getStr().equals("}")){
	    		curBlock.setLastIndex(count);
	    		for(Symbol s: curBlock.getSymbols()){
	    			s.setLastIndex(count);
	    		}
	    		Block block = curBlock.getOuter();
	    		curBlock = block;	
	    	}
	    }
		baseBlock.setLastIndex(code.size());
		for(Symbol s: baseBlock.getSymbols()){
			s.setLastIndex(count);
		}
	    base = baseBlock;
	}
	
	public void analizeSymbols(Block block, TreeSet<ResString> lexems) {
		ArrayList<ResString> code = new ArrayList<ResString>();
		code.addAll(lexems);
		int i = block.getFirstIndex();
		while(i < block.getLastIndex()){
			int j = i;
			for(Block inner: block.getInner()){
				if(i == inner.getFirstIndex()){
					analizeSymbols(inner, lexems);
					j = inner.getLastIndex();
				}
			}
	    	if(i==j){
	    		ResString lexem = code.get(i);
				if (lexem.getLex().equals("ids")) {
					Symbol symbol = new Symbol();
					symbol.setName(lexem.getStr());
					symbol.setBlockLevel(block.getLevel());
					symbol.setBlockType(block.getType());
					String type = code.get(i - 1).getStr();
					if (code.get(i - 1).getStr().equals(".") && code.get(i - 2).getLex().equals("ids")){
    					boolean bool = false;
						for(ClassName cn: classes){
    						bool |= cn.hasMethod(code.get(i).getStr()) || cn.hasProperty(code.get(i).getStr());
    					}
						if(!bool){
							throw new RuntimeException("Accessing private (lexem "+(i+1)+")");
						}
					}
					if (code.get(i - 1).getLex().equals("ids")
							||(type.equals("int") || type.equals("float")
							|| type.equals("boolean"))) {
						symbol.setDeclarated(true);
						Symbol check = block.find(code.get(i).getStr());
						if ((check != null) && ( i!= check.getFirstIndex()) && !(block.getName().equals(check.getName()))) {
								throw new RuntimeException("Variable already exists (lexem "+(i+1)+")");
						}
						symbol.setFirstIndex(i);
						symbol.setLastIndex(block.getLastIndex());
						if(block.getType() == BlockType.CLASS){
	    					symbol.setPrivateSymbol(code.get(i-2).getStr().equals("private"));
	    					for(ClassName cn: classes){
	    						if(cn.getName().getName().equals(block.getName())){
	    							cn.addProperty(symbol);
	    						}
	    					}
						}
						block.addSymbol(symbol);
					} else {
						Symbol check = block.find(symbol.getName());
						if ((check != null) && ( i!= check.getFirstIndex()) && !(block.getName().equals(check.getName()))) {
								check.isAlive(i);
						} else if (check == null){
							throw new RuntimeException("Undeclared variable (lexem "+(i+1)+")");	
						} 
					}
				}
				i++;
	    	} else {
	    		i = j;
	    	}
		}

	}

}
